﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TLang.Ast
{
    using Values;

    public class RecordDef : Node
    {
        public Name name;
        public List<Name> parents;
        public Scope propertyForm;
        public Scope properties;


        public RecordDef(Name name, List<Name> parents, Scope propertyForm,
                         String file, int start, int end, int line, int col) : base(file, start, end, line, col)
        {
            this.name = name;
            this.parents = parents;
            this.propertyForm = propertyForm;
        }


        public override Value Interp(Scope s)
        {
            Scope properties = Declare.evalProperties(propertyForm, s);

            if (parents != null)
            {
                foreach (Node p in parents)
                {
                    Value pv = p.Interp(s);
                    properties.PutAll(((RecordType)pv).properties);
                }
            }
            Value r = new RecordType(name.Id, this, properties);
            s.PutValue(name.Id, r);
            return r;
        }

        public override Value Typecheck(Scope s)
        {
            Scope properties = Declare.typecheckProperties(propertyForm, s);

            if (parents != null)
            {
                foreach (Node p in parents)
                {
                    Value pv = p.Typecheck(s);
                    if (!(pv is RecordType)) {
                        Util.Abort(p, "parent is not a record: " + pv);
                        return null;
                    }
                    Scope parentProps = ((RecordType)pv).properties;

                    // check for duplicated keys
                    foreach (String key in parentProps.Keys)
                    {
                        Value existing = properties.LookupLocalType(key);
                        if (existing != null)
                        {
                            Util.Abort(p, "conflicting field " + key +
                                    " inherited from parent " + p + ": " + pv);
                            return null;
                        }
                    }

                    // add all properties or all fields in parent
                    properties.PutAll(parentProps);
                }
            }

            Value r = new RecordType(name.Id, this, properties);
            s.PutValue(name.Id, r);
            return r;
        }


        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(Constants.PAREN_BEGIN);
            sb.Append(Constants.RECORD_KEYWORD).Append(" ");
            sb.Append(name);

            if (parents != null)
            {
                sb.Append(" (" + Node.PrintList(parents) + ")");
            }

            foreach (String field in propertyForm.Keys)
            {
                sb.Append("[" + field);
                Map<String, Object> props = propertyForm.LookupAllProps(field);
                foreach (var e in props)
                {
                    sb.Append(" :" + e.Key + " " + e.Value);
                }
                sb.Append("]");
            }

            sb.Append(Constants.PAREN_END);
            return sb.ToString();
        }
    }
}
